Object.assign(global, {
    INTENT_STACK:"INTENT_STACK",//标识一个IntentStack对象
    INTENT_SYNC_STACK:"INTENT_SYNC_STACK",//同步Stack
    INTENT_THROW:"INTENT_THROW",//抛出错误
    INTENT_THROWUP:"INTENT_THROWUP",//向上抛出错误，注意这会终止当前栈
    INTENT_STOP:"INTENT_STOP",//暂停

    INTENT_NO_CONTROL_OBJ:"INTENT_NO_CONTROL_OBJ",
    INTENT_NO_TARGET_FUNCTION:"INTENT_NO_TARGET_FUNCTION",

    INTENT_RETURN_FAIL:"INTENT_RETURN_FAIL",
});

module.exports.INTENT_STACK = "INTENT_STACK";
module.exports.INTENT_SYNC_STACK = "INTENT_SYNC_STACK";
module.exports.INTENT_THROW = "INTENT_THROW";
module.exports.INTENT_THROWUP = "INTENT_THROWUP";
module.exports.INTENT_STOP = "INTENT_STOP";
module.exports.INTENT_NO_CONTROL_OBJ = "INTENT_NO_CONTROL_OBJ";
module.exports.INTENT_NO_TARGET_FUNCTION = "INTENT_NO_TARGET_FUNCTION";
module.exports.INTENT_RETURN_FAIL = "INTENT_RETURN_FAIL";

let abstractFunction = function(apply,list){
    for(let name of list){
        let target = apply[name];
        if(target == undefined || target.constructor.name != "Function"){
            throw ReferenceError(`${apply.constructor.name} 需要实现抽象方法: ${name}`)
        }
    }
}

let Exception = class Exception{
    constructor(code,intent,trace){
        this.code = code;
        this.intent = intent;
        this.trace = trace;
        this.sendEmail = false;
    }

    printStackTrace(customMsg){
        let trace = "";
        for(let i of this.trace){
            trace += `    at ${i}\n`
        }

        let msg = `    when ${JSON.stringify(this.intent)}\n${trace}</span>`;
        if(customMsg != undefined){
            msg = `<span style="color:#ff5722">[Intent] ${customMsg}\nTick:${Game.time}\nException: ${this.code}\n`+msg;
        }else{
            msg = `<span style="color:#ff5722">[Intent] Exception: ${this.code}\n` + msg;
        }
        console.log(msg);

        if(this.sendEmail){
            Game.notify(msg);
        }
    }
}
let Intent = class Intent{
    constructor(act,...data){
        this.act = act;
        this.data = data;
    }

    getAction(){
        return this.act;
    }

    setAction(act){
        this.act = act;
    }

    getData(){
        return this.data;
    }

    setData(dataArray){
        this.data = dataArray;
    }
}

let IntentEvent = class IntentEvent{
    constructor(targetIntentObject,intent){
        this.target = targetIntentObject;
        this.intent = intent;
    }

    getIntentObject(){
        return this.target;
    }

    getIntent(){
        return this.intent;
    }
}

let IntentEventListener = class IntentEventListener{

    constructor(targetBUS,listener){
        this.listeners = [];
        if(listener != undefined){
            this.listener = listener;
        }
        if(targetBUS != undefined && targetBUS instanceof IntentEventListener){
            targetBUS.registry(this);
        }
    }

    //ToOverride
    listener(){

    }

    handleEvent(event){
        for(let i of this.listeners){
            i.listener(event);
        }
    }

    registry(listener){
        if(listener instanceof IntentEventListener){
            this.listeners.push(listener);
        }
    }
}

let _IntentEventListenerBUS = new IntentEventListener();


let IntentStack = class IntentStack{
    constructor(intentObj,data = []){//实例化时可能传递的数据
        this.name = "Anonymous"
        this.stack = []; //存在4种类型
        /**
         * Intent 对象
         * IntentStack 对象
         * IntentStack 类
         */

        this.exception = this.exception || function(){};
        this.finished = this.finished || function(){};

        this.intentObject = intentObj;

        this.autoNext = true;

        this.syncState = false;
    }

    syncNext(){
        if(this.stack.length == 0){
            //为了安全 Sync Stack 不执行Finished方法
            return false;
        }

        while(this.stack.length > 0){
            if(this.stack[0] instanceof Intent){
                this.handleIntent(this.stack.shift());
                continue;
            }
    
            if(this.stack[0] instanceof IntentStack){
                this.stack[0].syncNext();
                this.stack.shift();
                continue;
            }
    
            if(IntentStack.isPrototypeOf(this.stack[0])){
                //实例化并触发
                let intentStackClass = this.stack.shift();
                let intentStack = new intentStackClass(this.getIntentObject());
                this.stack.unshift(intentStack);
                this.syncNext();
            }
        }
        return true;
    }


    next(callStack){
        if(this.isSync()){
            return this.syncNext();
        }
        if(this.stack.length == 0){
            //触发finished
            let appendNum = this.handleFinished();
            if(appendNum > 0){
                if(this.autoNext){
                    return this.next();
                }else{
                    return true;
                }
            }
            return false;
        }


        if(this.stack[0] instanceof Intent){
            return this.handleIntent(this.stack.shift(),callStack) || true;
        }

        if(this.stack[0] instanceof IntentStack){
            let result = this.stack[0].next(this);
            //result 为 false 说明对应的intentStack空了 或 出现上报异常了
            if(result == true){
                return true;
            }
            this.stack.shift();

            if(result instanceof Exception){
                
                let appendNum = this.handleException(result);
                if(appendNum > 0){
                    if(this.autoNext){
                        return this.next();
                    }
                }
                return true;
            }
            
            if(this.autoNext){
                return this.next();
            }else{
                return true;
            }
        }

        if(IntentStack.isPrototypeOf(this.stack[0])){
            //实例化并触发
            let intentStackClass = this.stack.shift();
            let intentStack;
            try{
                intentStack = new intentStackClass(this.getIntentObject());
            }catch(e){
                let intentObj = this.getIntentObject();
                let trace = (intentObj == undefined) ? "" : intentObj.getStackTrace();
                new Exception(
                    "unknow",
                    "运行错误",
                    trace
                ).printStackTrace(`我们实例化行为时遇到一个运行错误，请检查您的逻辑行为 目标IntentObject: ${intentObj.name}\n错误详情:${e.stack}`);
                this.getIntentObject().clean();
                return;
            }
            this.stack.unshift(intentStack);
            if(this.autoNext){
                //console.log("Stack创建并Next" + Game.time + this.getIntentObject().name);
                return this.next();
            }else{
                return true;
            }
        }
    }

    

    handleIntent(intent,callStack){

        _IntentEventListenerBUS.handleEvent(new IntentEvent(this.getIntentObject(),intent));

        let act = intent.getAction();
        let data = intent.getData();

        //TODO 处理特殊Intent
        if(act == INTENT_STACK){
            let intentStack = data.shift();
            //判断类型
            if(IntentStack.isPrototypeOf(intentStack)){
                //实例化并触发
                let intentStackClass = intentStack
                try{
                    intentStack = new intentStackClass(this.getIntentObject(),data);
                }catch(e){
                    new Exception(
                        "unknow",
                        "运行错误",
                        this.getIntentObject().getStackTrace()
                    ).printStackTrace(`我们遇到一个运行错误，请检查您的逻辑行为\n错误详情:${e.stack}`);
                    this.getIntentObject().clean();
                    return;
                }
                this.stack.unshift(intentStack);
                if(this.autoNext){
                    this.next();
                }
                return;
            }
            
            if(intentStack instanceof IntentStack){
                this.stack.unshift(intentStack);
                if(intentStack.stack.length > 0){
                    if(this.autoNext){
                        this.next();
                    }
                }
                return;
            }
            return;
        }

        if(act == INTENT_SYNC_STACK){
            let intentStack = data.shift();
            //判断类型
            if(IntentStack.isPrototypeOf(intentStack)){
                //实例化并触发
                let intentStackClass = intentStack
                try{
                    intentStack = new intentStackClass(this.getIntentObject(),data);
                }catch(e){
                    new Exception(
                        "unknow",
                        "运行错误",
                        this.getIntentObject().getStackTrace()
                    ).printStackTrace(`我们遇到一个运行错误，请检查您的逻辑行为\n错误详情:${e.stack}`);
                    this.getIntentObject().clean();
                    return;
                }
                intentStack.setSync(true);
                this.stack.unshift(intentStack);
                if(intentStack.stack.length > 0){
                    if(this.autoNext){
                        this.next();
                    }
                }
                return;
            }
            
            if(intentStack instanceof IntentStack){
                intentStack.setSync(true);
                this.stack.unshift(intentStack);
                if(intentStack.stack.length > 0){
                    if(this.autoNext){
                        this.next();
                    }
                }
                return;
            }
            return;
        }

        if(act == INTENT_THROW){
            let type = data[0];
            let appendNum = this.handleException(new Exception(
                type,
                intent,
                this.getIntentObject().getStackTrace()
            ))
            if(appendNum > 0){
                if(this.autoNext){
                    this.next();
                }
            }
            return;
        }

        if(act == INTENT_THROWUP){
            let type = data[0];
            return new Exception(
                type,
                intent,
                this.getIntentObject().getStackTrace()
            );
        }

        if(act == INTENT_STOP){
            let left = data[0];
            left--;
            if(left > 0){
                this.stack.unshift(new Intent(INTENT_STOP,left));
            }
            return;
        }

        let controlObj;
        try{
            controlObj = this.getIntentObject().getControlObj();
        }catch(e){
            console.log(e);
        }
        if(controlObj == undefined){
            //Throw
            let appendNum = this.handleException(new Exception(
                INTENT_NO_CONTROL_OBJ,
                intent,
                this.getIntentObject().getStackTrace()
            ))
            if(appendNum > 0){
                if(this.autoNext){
                    this.next();
                }
            }
            return;
        }

        let result;
        try{
            result = controlObj[act].apply(controlObj,data);
        }catch(e){
            let appendNum = this.handleException(new Exception(
                INTENT_NO_TARGET_FUNCTION,
                intent,
                this.getIntentObject().getStackTrace()
            ))
            if(appendNum > 0){
                if(this.autoNext){
                    this.next();
                }
            }
            return;
        }

        if(result != OK){
            let appendNum = this.handleException(new Exception(
                result,
                intent,
                this.getIntentObject().getStackTrace()
            ))
            if(appendNum > 0){
                if(this.autoNext){
                    this.next();
                }
            }
        }
    }

    handleException(exception){
        //先暂存当前剩下的stack
        let leftStack = this.stack;
        //清空stack
        this.stack = [];
        //让exception处理stack
        let result = this.exception(exception);
        if(result == INTENT_RETURN_FAIL){
            this.getIntentObject().clean();
            return 0;
        }
        //记录更改
        let appendNum = this.stack.length;
        //合并stack
        while(leftStack.length > 0){
            this.stack.push(leftStack.pop());
        }
        return appendNum;
    }

    handleFinished(){
        //先暂存当前剩下的stack
        let leftStack = this.stack;
        //清空stack
        this.stack = [];
        //让exception处理stack
        try{
            this.finished();
        }catch(e){
            new Exception(
                "unknow",
                "运行错误",
                this.getIntentObject().getStackTrace()
            ).printStackTrace(`我们遇到一个运行错误，请检查您的逻辑行为\n错误详情:${e.stack}`);
            this.getIntentObject().clean();
            return;
        }
        //记录更改
        let appendNum = this.stack.length;
        //合并stack
        while(leftStack.length > 0){
            this.stack.push(leftStack.pop());
        }
        return appendNum;
    }

    push(obj){
        if(obj instanceof Intent){
            this.stack.push(obj);
            return;
        }

        if(obj instanceof IntentStack){
            this.stack.push(obj);
            return;
        }

        if(IntentStack.isPrototypeOf(obj)){
            this.stack.push(obj);
            return;
        }
    }

    setStackName(name){
        this.name = name;
    }

    getIntentObject(){
        return this.intentObject;
    }

    setIntentObject(intentObject){
        this.intentObject = intentObject;
    }

    setSync(bool){
        this.syncState = bool;
    }

    isSync(){
        if(this.syncState){
            return true;
        }

        return false;
    }

    clean(){
        this.stack = [];
    }
}

let IntentObject = class IntentObject{
    constructor(){
        this.name = this.name || "unknow target";

        abstractFunction(this,[
            "getControlObj"
        ])

        this.rootStack = undefined;
    }

    getName(){
        return this.name;
    }


    next(){
        if(this.rootStack == undefined){
            return;
        }

        if(!this.rootStack.next()){
            this.rootStack = undefined;
        }
    }

    do(obj,...data){// 接受 IntentStack Intent
        if(this.rootStack == undefined){
            if(obj instanceof IntentStack){
                obj.setIntentObject(this);
                this.rootStack = obj;
                return;
            }
            this.rootStack = new IntentStack(this);
            this.rootStack.setStackName("RootStack");
        }

        if(data.length > 0){//封装数据后再push
            data.unshift(obj);
            let intent = new Intent();
            intent.setAction(INTENT_STACK);
            intent.setData(data);
            this.rootStack.push(intent);
        }else{//直接push
            this.rootStack.push(obj)
        }

    }

    set(obj){// 接受 IntentStack Intent 但多了一个清除当前行为的调用
        this.clean();
        this.do(obj);
    }

    isFree(){
        return this.rootStack == undefined
    }

    clean(){
        this.rootStack = undefined;
    }

    printStack(config = {
        x:10,
        y:10,
        v :new RoomVisual(),
        style:{font:0.5,align:"left",},
        details:false
    }){
        let {x,y,v,style,details} = config;
        v.text(`Stack of ${this.name}: ${JSON.stringify(this.getStackTrace())}`,x,y++,style);
        if(this.rootStack == undefined){
            return;
        }

        let draw = function(stack){
            v.text(`${stack.name} ${stack.stack.length}`,x,y++,style);
            let printIntent = function(i){
                if(i instanceof Intent){
                    if(i.getAction() == INTENT_STACK || i.getAction() == INTENT_SYNC_STACK){
                        let intent = new Intent();
                        intent.act = i.getAction() + "";
                        intent.data = [].concat(i.getData());
                        intent.data[0] = intent.data[0].name;
                        v.text(JSON.stringify(intent),x,y++,style);
                    }else{
                        v.text(JSON.stringify(i),x,y++,style);
                    }
                }
                if(i instanceof IntentStack){
                    x++;
                    draw(i);
                    x--;
                }
                if(IntentStack.isPrototypeOf(i)){
                    v.text(i.name + " <IntentStack>",x,y++,style);
                }
            }

            //判断是否打印细节
            let printdetails = function(){
                for(let i of stack.stack){
                    printIntent(i);
                }
            }
            if(details){
                printdetails();
            }else{//只打印前5和后5，中间省略
                if(stack.stack.length <= 6){
                    printdetails();
                    return;
                }
                let count = 0;
                let left = stack.stack.length;
                for(let i of stack.stack){
                    count++;
                    left--;
                    if(count <=3 || left < 3){
                        printIntent(i);
                    }
                    if(count == 3){
                        v.text(`. . . (${stack.stack.length-6})`,x,y++,style);
                    }
                }
            }
            
        }
        draw(this.rootStack);

        
    }

    getStackTrace(){
        let buffer = []
        if(this.rootStack == undefined){
            return buffer;
        }
        // console.log(this.rootStack[0].name)
        let record = function(stack){
            let namespace;
            try{
                namespace =stack.constructor.namespace();
            }catch(e){}
            if(namespace != undefined){
                buffer.unshift(`${stack.name} (${namespace})`);
            }else{
                buffer.unshift(`${stack.name}`);
            }
            if(stack.stack.length == 0){
                return;
            }
            if(stack.stack[0] instanceof IntentStack){
                record(stack.stack[0]);
                return;
            }
        }
        record(this.rootStack);
        return buffer;
    }

    

}




module.exports.Exception = Exception;
module.exports.Intent = Intent;
module.exports.IntentStack = IntentStack;
module.exports.IntentObject = IntentObject;
module.exports.IntentEvent = IntentEvent;
module.exports.IntentEventListener = IntentEventListener;
module.exports.IntentEventBUS = _IntentEventListenerBUS;